package basicutil.dynamic;

import lombok.extern.slf4j.Slf4j;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.lang.instrument.IllegalClassFormatException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

@Slf4j
// javac -cp 指定classpath时，如果依赖class文件，要填class文件所在路径-package路径，不能填class文件的绝对路径全路径！
public class DynamicCompileUtil {


    // todo 编译多个文件，这些文件可能依赖同包下的其他class或者jar，这些文件的运行情况可能依赖特定的resource
    // todo 绕过文件直接编译字符串
    public static Class<?> getClassFromFile(String fileDir, String cp, String res, String targetClassName) throws Exception {
        return null;
    }


    /*
    使用攻略：
    filepath传一个.java文件或目录
    classPath和javac命令行的cp参数一致：可以用分号分隔多个jar或者path；请参考JdkDynamicCompileClassLoader.addClassPath
     */
    public static Class<?> getClassFromFile(String targetClassName, String filePath, String classPath) throws Exception {
        JdkDynamicCompileClassLoader classLoader = compile(new String[]{filePath}, classPath);
        classLoader.loadClass(targetClassName);
        Class<?> klass = classLoader.loadClass(targetClassName);

        return klass;
    }

    /*
    使用攻略：
    filepath传一个.java文件或目录
    classPath和javac命令行的cp参数一致：可以用分号分隔多个jar或者path；请参考org.etry.compile.dynamic2.dynamic.JdkDynamicCompileClassLoader.addClassPath
     */
    public static JdkDynamicCompileClassLoader compile(String[] filePath, String classPath) throws Exception {
        log.info("compile filePath: {}, class path: {}", filePath, classPath);
        classPath = classPath == null ? "" : classPath;
        // 获取系统编译器实例
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();


        // 获取标准的Java文件管理.器实例
        DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
        StandardJavaFileManager manager = compiler.getStandardFileManager(diagnosticCollector, null, null);
        List<File> fileList = new ArrayList<>();
        for (String s : filePath) {
            File file = new File(s);
            if (file.isDirectory()) {
                Files.walk(file.toPath())

                        .map(Path::toFile).filter(f -> !f.isDirectory())
                        .filter(f -> f.getName().endsWith(".java"))
                        .forEach(fileList::add);

            } else {
                fileList.add(file);
            }
        }

        Iterable<? extends JavaFileObject> javaFileObjects;
        javaFileObjects = manager.getJavaFileObjects(fileList.toArray(new File[0]));
        for (JavaFileObject javaFileObject : javaFileObjects) {
            log.debug("file to compile: {}", javaFileObject.getName());
        }


        // 初始化自定义类加载器
        JdkDynamicCompileClassLoader classLoader = new JdkDynamicCompileClassLoader();
        for (String s : classPath.split(";")) {
            classLoader.addClassPath(s);
        }

        // 初始化自定义Java文件管理器实例
        JdkDynamicCompileJavaFileManager fileManager = new JdkDynamicCompileJavaFileManager(manager, classLoader);

        List<String> options = new ArrayList<>();
        options.add("-cp");
        options.add(classPath + ";" + System.getProperty("java.class.path"));
        JavaCompiler.CompilationTask compilationTask = compiler.getTask(
                null,
                fileManager,
                diagnosticCollector,
                options,
                null,
                javaFileObjects
        );
        // 执行编译任务
        Boolean result = compilationTask.call();

        // 编译结果：
        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()) {
            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
                throw new IllegalClassFormatException(diagnostic.toString());
            }

        }
        manager.close();
        fileManager.close();
        return classLoader;
    }

}
